


#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "XMen_asn1.h"

/*
 * Encode length field in ASN.1 DER encoding to byte stream
 * len: length value to encode
 * out: pointer to output buffer pointer
 * outlen: output length
 * return: 1 on success, -1 on failure
 */
int XMen_lib_asn1_sm2_len_to_der(uint32_t len, uint8_t** out, uint32_t* outlen)
{
    uint8_t buf[4];
    int nbytes;

    /* Parameter validation */
    if (len > INT_MAX || !outlen) {
        return -1;
    }

    /* Short form: length < 128, use single byte */
    if (len < 128u)
    {
        if (out && *out)
        {
            *(*out)++ = (uint8_t)len;
        }
        (*outlen)++;
    }
    else
    {
        /* Long form: calculate number of bytes needed */
        if (len < 256u)
        {
            nbytes = 1;
        }
        else if (len < 65536u)
        {
            nbytes = 2;
        }
        else if (len < (1 << 24))
        {
            nbytes = 3;
        }
        else
        {
            nbytes = 4;
        }

        /* Convert length to big-endian bytes */
        PUTU32(buf, (uint32_t)len);

        /* Write length bytes if output buffer provided */
        if (out && *out) {
            *(*out)++ = 0x80 + nbytes;  /* First byte: 0x80 + number of length bytes */
            memcpy(*out, buf + 4 - nbytes, nbytes);  /* Copy length bytes */
            (*out) += nbytes;
        }
        (*outlen) += 1 + nbytes;
    }

    /* Success */
    return 1;
}

int XMen_lib_asn1_sm2_length_from_der(uint32_t* len, const uint8_t** in, uint32_t* inlen)
{
    uint8_t buf[4] = { 0 };

    /* Parameter validation */
    if (!len || !in || !(*in) || !inlen || (*inlen == 0u)) {
        return -1;
    }

    /* Handle short form (length < 128) */
    if (**in < 128) {
        *len = *(*in)++;
        (*inlen)--;
    } else {
        /* Long form: get number of length bytes */
        int nbytes = *(*in)++ & 0x7f;
        (*inlen)--;

        /* Validate number of bytes */
        if (nbytes < 1 || nbytes > 4 || *inlen < (uint32_t)nbytes) {
            return -1;
        }

        /* Read length bytes into buffer and convert from big-endian */
        memcpy(buf + 4 - nbytes, *in, nbytes);
        *len = (uint32_t)GETU32(buf);
        *in += nbytes;
        *inlen -= nbytes;
    }

    /* Ensure remaining input is long enough */
    if (*inlen < *len) {
        return -2;
    }

    /* Success */
    return 1;
}

int XMen_lib_asn1_sm2_data_to_der(const uint8_t* data, uint32_t datalen, uint8_t** out, uint32_t* outlen)
{
    /* Parameter validation */
    if (!outlen) {
        return -1;
    }

    /* Handle empty data */
    if (datalen == 0u) {
        return 0;
    }

    /* Write data if output buffer provided */
    if (out && *out) {
        /* Validate data pointer */
        if (!data) {
            return -1;
        }

        /* Copy data to output buffer */
        memcpy(*out, data, datalen);
        *out += datalen;
    }

    /* Update output length */
    *outlen += datalen;

    /* Success */
    return 1;
}



/*
 * Encode ASN.1 header with tag and length
 *
 * @param[in] tag     ASN.1 tag value
 * @param[in] dlen    Length of data that follows
 * @param[out] out    Output buffer to write encoded header, NULL for length only
 * @param[out] outlen Length of encoded output
 * @return 1 on success, -1 on error
 */
int XMen_lib_asn1_sm2_header_to_der(int tag, uint32_t dlen, uint8_t** out, uint32_t* outlen)
{
	if (!outlen) {
		return -1;
	}

	if (out && *out) {
		*(*out)++ = (uint8_t)tag;
	}
	(*outlen)++;

	(void)XMen_lib_asn1_sm2_len_to_der(dlen, out, outlen);
	return 1;
}

/*
 * Encode ASN.1 type with tag, length and data
 *
 * @param[in] tag     ASN.1 tag value
 * @param[in] d       Data to encode
 * @param[in] dlen    Length of data
 * @param[out] out    Output buffer to write encoded data, NULL for length only
 * @param[out] outlen Length of encoded output
 * @return 1 on success, 0 if data is NULL and dlen is 0, -1 on error
 */
int XMen_lib_asn1_sm2_type_to_der(int tag, const uint8_t* d, uint32_t dlen, uint8_t** out, uint32_t* outlen)
{
    /* Check output length parameter */
    if (!outlen) {
        return -1;
    }

    /* Handle NULL data case */
    if (!d) {
        if (dlen) {
            return -1;
        }
        return 0;
    }

    /* Write tag byte if output buffer provided */
    if (out && *out) {
        *(*out)++ = (uint8_t)tag;
    }
    (*outlen)++;

    /* Encode and write length */
    (void)XMen_lib_asn1_sm2_len_to_der(dlen, out, outlen);

    /* Write data if output buffer provided */
    if (out && *out) {
        memcpy(*out, d, dlen);
        *out += dlen;
    }
    *outlen += dlen;

    return 1;
}

/*
 * Decode ASN.1 type with tag, length and data
 *
 * @param[in] tag     Expected ASN.1 tag value
 * @param[out] d      Pointer to decoded data
 * @param[out] dlen   Length of decoded data
 * @param[in,out] in  Input buffer to decode from, updated to point after decoded data
 * @param[in,out] inlen Length of remaining input, updated after decoding
 * @return 1 on success, 0 if tag doesn't match, -1 on error
 */
int XMen_lib_asn1_sm2_type_from_der(int tag, const uint8_t** d, uint32_t* dlen, const uint8_t** in, uint32_t* inlen)
{
	/* Validate parameters */
	if (!d || !dlen || !in || !(*in) || !inlen) {
		return -1;
	}

	/* Check tag matches */
	if (*inlen == 0u || **in != tag) {
		*d = NULL;
		*dlen = 0;
		return 0;
	}
	(*in)++;
	(*inlen)--;

	/* Decode length */
	if (XMen_lib_asn1_sm2_length_from_der(dlen, in, inlen) != 1) {
		return -1;
	}

	/* Set data pointer and update input buffer */
	*d = *in;
	*in += *dlen;
	*inlen -= *dlen;
	return 1;
}

/*
 * Encode ASN.1 type with tag, length and non-empty data
 *
 * @param[in] tag     ASN.1 tag value to encode
 * @param[in] d       Data to encode
 * @param[in] dlen    Length of data to encode
 * @param[out] out    Output buffer to write encoded data to, NULL to get length only
 * @param[out] outlen Length of encoded data written to output buffer
 * @return 1 on success, -1 on error
 */
int XMen_lib_asn1_sm2_nonempty_type_to_der(int tag, const uint8_t* d, uint32_t dlen, uint8_t** out, uint32_t* outlen)
{
	int ret;

	if (d && dlen == 0u) {

		return -1;
	}
	if ((ret = XMen_lib_asn1_sm2_type_to_der(tag, d, dlen, out, outlen)) != 1) {
		if (ret)
			return ret;
	}
	return 1;
}

/*
 * Decode ASN.1 type with tag, length and non-empty data
 *
 * @param[in] tag     Expected ASN.1 tag value
 * @param[out] d      Pointer to decoded data
 * @param[out] dlen   Length of decoded data
 * @param[in,out] in  Input buffer to decode from, updated to point after decoded data
 * @param[in,out] inlen Length of remaining input, updated after decoding
 * @return 1 on success, 0 if tag doesn't match, -1 on error
 */
int XMen_lib_asn1_sm2_nonempty_type_from_der(int tag, const uint8_t** d, uint32_t* dlen, const uint8_t** in, uint32_t* inlen)
{
	int ret;

	if ((ret = XMen_lib_asn1_sm2_type_from_der(tag, d, dlen, in, inlen)) != 1) {
		if (ret)
			return ret;
	}
	if (*dlen == 0u) {

		return -1;
	}
	return 1;
}

/*
 * Decode ASN.1 type with any tag, length and data
 *
 * @param[out] tag    Decoded ASN.1 tag value
 * @param[out] d      Pointer to decoded data
 * @param[out] dlen   Length of decoded data
 * @param[in,out] in  Input buffer to decode from, updated to point after decoded data
 * @param[in,out] inlen Length of remaining input, updated after decoding
 * @return 1 on success, 0 if no data available, -1 on error
 */
int XMen_lib_asn1_sm2_any_type_from_der(int* tag, const uint8_t** d, uint32_t* dlen, const uint8_t** in, uint32_t* inlen)
{
	if (!tag || !d || !dlen || !in || !(*in) || !inlen) {

		return -1;
	}

	if (*inlen == 0u) {
		*tag = -1;
		*d = NULL;
		*dlen = 0;
		return 0;
	}

	*tag = *(*in)++;
	(*inlen)--;

	if (XMen_lib_asn1_sm2_length_from_der(dlen, in, inlen) != 1) {

		return -1;
	}

	*d = *in;
	*in += *dlen;
	*inlen -= *dlen;
	return 1;
}

/*
 * Encode raw ASN.1 data
 *
 * @param[in] a       Raw ASN.1 data to encode
 * @param[in] alen    Length of raw ASN.1 data
 * @param[out] out    Output buffer to write encoded data to, NULL to get length only
 * @param[out] outlen Length of encoded data written to output buffer
 * @return 1 on success, 0 if no data provided, -1 on error
 */
int XMen_lib_asn1_sm2_any_to_der(const uint8_t* a, uint32_t alen, uint8_t** out, uint32_t* outlen)
{
	if (!outlen) {

		return -1;
	}

	if (!a) {
		if (a) {

			return -1;
		}
		return 0;
	}

	if (out && *out) {
		memcpy(*out, a, alen);
		*out += alen;
	}
	*outlen += alen;

	return 1;
}

/*
 * Decode raw ASN.1 data from DER encoding
 *
 * @param[out] a      Pointer to store decoded raw ASN.1 data
 * @param[out] alen   Length of decoded raw ASN.1 data
 * @param[in,out] in  Input buffer containing DER encoded data
 * @param[in,out] inlen Length of input buffer, updated with remaining bytes
 * @return 1 on success, -1 on error
 */
int XMen_lib_asn1_sm2_any_from_der(const uint8_t** a, uint32_t* alen, const uint8_t** in, uint32_t* inlen)
{
	int ret;
	int tag;
	const uint8_t* d;
	uint32_t dlen;

	if (!a || !alen || !in || !(*in) || !inlen) {
		return -1;
	}
	*a = *in;
	*alen = *inlen;

	if ((ret = XMen_lib_asn1_sm2_any_type_from_der(&tag, &d, &dlen, in, inlen)) != 1) {
		if (ret)
			return ret;
	}
	*alen -= *inlen;

	return 1;
}

/*
 * Encode ASN.1 INTEGER to DER encoding with custom tag
 *
 * @param[in] tag     ASN.1 tag value to use
 * @param[in] a       Integer value to encode as byte array
 * @param[in] alen    Length of integer byte array
 * @param[out] out    Output buffer to write encoded data, NULL for length only
 * @param[out] outlen Length of encoded output
 * @return 1 on success, 0 if no data provided, -1 on error
 */
int XMen_lib_asn1_sm2_integer_to_der_ex(int tag, const uint8_t* a, uint32_t alen, uint8_t** out, uint32_t* outlen)
{
    /* Parameter validation */
	if (!outlen) {
		return -1;
	}

    /* Handle NULL data case */
	if (!a) {
		return 0;
	}

    /* Validate length */
	if (alen <= 0u || alen > INT_MAX) {
		return -1;
	}

    /* Write tag byte */
	if (out && *out) {
		*(*out)++ = tag;
	}
	(*outlen)++;

    /* Skip leading zeros */
	while (*a == 0 && alen > 1u) {
		a++;
		alen--;
	}

    /* Handle sign bit */
	if (a[0] & 0x80) {
        /* Add leading zero byte to preserve sign */
		XMen_lib_asn1_sm2_len_to_der(alen + 1u, out, outlen);
		if (out && *out) {
			*(*out)++ = 0x00;
			memcpy(*out, a, alen);
			(*out) += alen;
		}
		(*outlen) += 1 + alen;
	}
	else {
        /* Write length and data directly */
		XMen_lib_asn1_sm2_len_to_der(alen, out, outlen);
		if (out && *out) {
			memcpy(*out, a, alen);
			(*out) += alen;
		}
		(*outlen) += alen;
	}

    /* Success */
	return 1;
}

/*
 * Decode ASN.1 INTEGER from DER encoding with custom tag
 *
 * @param[in] tag     Expected ASN.1 tag value
 * @param[out] a      Pointer to store decoded integer value
 * @param[out] alen   Length of decoded integer value
 * @param[in,out] in  Input buffer containing DER encoded data
 * @param[in,out] inlen Length of input buffer, updated with remaining bytes
 * @return 1 on success, 0 if no data, -1 on error
 */
int XMen_lib_asn1_sm2_integer_from_der_ex(int tag, const uint8_t** a, uint32_t* alen, const uint8_t** in, uint32_t* inlen)
{
	uint32_t len;

    /* Parameter validation */
	if (!a || !alen || !in || !(*in) || !inlen) {
		return -1;
	}

    /* Check tag */
	if ((*inlen == 0u) || (**in != tag)) {
		*a = NULL;
		*alen = 0;
		return 0;
	}
	(*in)++;
	(*inlen)--;

    /* Get length */
	if (XMen_lib_asn1_sm2_length_from_der(&len, in, inlen) != 1) {
		return -1;
	}
	if (len == 0u) {
		return -1;
	}

    /* Check sign bit */
	if (**in & 0x80) {
		return -1;
	}

    /* Skip redundant leading zero */
	if (**in == 0u && len > 1u) {
		(*in)++;
		(*inlen)--;
		len--;

		if (((**in) & 0x80) == 0) {
			return -1;
		}
	}

    /* Validate leading zeros */
	if (**in == 0u && len > 1u) {
		return -1;
	}

    /* Set output parameters */
	*a = *in;
	*alen = len;
	*in += len;
	*inlen -= len;

    /* Success */
	return 1;
}

/*
 * Convert OID node value to base-128 encoding
 *
 * @param[in] a       OID node value to encode
 * @param[out] out    Output buffer to write encoded bytes, NULL for length only
 * @param[out] outlen Length of encoded output
 */
static void XMen_lib_asn1_sm2_oid_node_to_base128(uint32_t a, uint8_t** out, uint32_t* outlen)
{
	uint8_t buf[5];
	int n = 0;

	buf[n++] = a & 0x7fu;
	a >>= 7;

	while (a) {
		buf[n++] = 0x80 | (a & 0x7fu);
		a >>= 7;
	}

	while (n--) {
		if (out && *out) {
			*(*out)++ = buf[n];
		}
		(*outlen)++;
	}
}

/*
 * Convert OID nodes array to DER encoded octets
 *
 * @param[in] nodes      Array of OID node values
 * @param[in] nodes_cnt  Number of nodes in array
 * @param[out] out       Output buffer to write encoded bytes
 * @param[out] outlen    Length of encoded output
 * @return 1 on success, -1 on error
 */
int XMen_lib_asn1_sm2_object_identifier_to_octets(const uint32_t* nodes, uint32_t nodes_cnt, uint8_t* out, uint32_t* outlen)
{
	if (!nodes || !outlen) {
		return -1;
	}
	if (nodes_cnt < ASN1_OID_MIN_NODES || nodes_cnt > ASN1_OID_MAX_NODES) {
		return -1;
	}
	if (out) {
		*out++ = (uint8_t)(nodes[0] * 40 + nodes[1]);
	}
	(*outlen) = 1;
	nodes += 2;
	nodes_cnt -= 2u;

	while (nodes_cnt--) {
		XMen_lib_asn1_sm2_oid_node_to_base128(*nodes++, &out, outlen);
	}
	return 1;
}

/*
 * Decode base-128 encoded OID node value
 *
 * @param[out] a      Decoded OID node value
 * @param[in,out] in  Input buffer containing encoded bytes, updated after decoding
 * @param[in,out] inlen Length of remaining input, updated after decoding
 * @return 1 on success, -1 on error
 */
static int XMen_lib_asn1_sm2_oid_node_from_base128(uint32_t* a, const uint8_t** in, uint32_t* inlen)
{
	uint8_t buf[5];
	int n = 0;
	int i;

    /* Read bytes until one without high bit set */
	for (;;) {
		if ((*inlen)-- < 1 || n >= 5) {
            /* Not enough input or too many bytes */
			return -1;
		}
		buf[n] = *(*in)++;
		if ((buf[n++] & 0x80) == 0) {
            /* Found last byte */
			break;
		}
	}

    /* Check for overflow */
	if (n == 5 && (buf[0] & 0x70)) {
		return -1;
	}

	*a = 0;
    /* Combine bytes into value */
	for (i = 0; i < n; i++) {
		*a = ((*a) << 7) | (buf[i] & 0x7f);
	}

    /* Success */
	return 1;
}

/*
 * Convert ASN.1 OID octets to array of node values
 *
 * @param[out] nodes      Array to store OID node values, NULL to get count only
 * @param[out] nodes_cnt  Number of nodes in OID
 * @param[in] in         Input buffer containing encoded OID octets
 * @param[in] inlen      Length of input buffer
 * @return 1 on success, -1 on error
 */
int XMen_lib_asn1_sm2_object_identifier_from_octets(uint32_t* nodes, uint32_t* nodes_cnt, const uint8_t* in, uint32_t inlen)
{
	if (!nodes_cnt || !in || !inlen) {
		return -1;
	}

	if (nodes) {
		*nodes++ = (*in) / 40;
		*nodes++ = (*in) % 40;
	}
	in++;
	inlen--;
	*nodes_cnt = 2;

	while (inlen) {
		uint32_t val;
		if (*nodes_cnt > ASN1_OID_MAX_NODES) {
			return -1;
		}
		if (XMen_lib_asn1_sm2_oid_node_from_base128(&val, &in, &inlen) < 0) {
			return -1;
		}
		if (nodes) {
			*nodes++ = val;
		}
		(*nodes_cnt)++;
	}

	return 1;
}

/*
 * Decode ASN.1 OBJECT IDENTIFIER from DER encoding with custom tag
 *
 * @param[in] tag        ASN.1 tag value to use
 * @param[out] nodes     Array to store decoded OID node values
 * @param[out] nodes_cnt Number of nodes in decoded OID
 * @param[in,out] in     Input buffer containing DER encoded data, updated after decoding
 * @param[in,out] inlen  Length of remaining input, updated after decoding
 * @return 1 on success, 0 if tag doesn't match, -1 on error
 */
int XMen_lib_asn1_sm2_object_identifier_from_der_ex(int tag, uint32_t* nodes, uint32_t* nodes_cnt,
	const uint8_t** in, uint32_t* inlen)
{
	uint32_t len;

    /* Parameter validation */
	if (!nodes || !nodes_cnt || !in || !(*in) || !inlen) {
		return -1;
	}

    /* Check tag matches */
	if (*inlen == 0u || **in != tag) {
		*nodes_cnt = 0;
		return 0;
	}
	(*in)++;
	(*inlen)--;

    /* Decode length */
	if (XMen_lib_asn1_sm2_length_from_der(&len, in, inlen) != 1) {
		return -1;
	}

    /* Validate minimum length */
	if (len < ASN1_OID_MIN_OCTETS) {
		return -1;
	}

    /* Convert octets to node values */
	if (XMen_lib_asn1_sm2_object_identifier_from_octets(nodes, nodes_cnt, *in, len) != 1) {
		return -1;
	}

    /* Update input buffer */
	*in += len;
	*inlen -= len;

    /* Success */
	return 1;
}

/*
 * Compare two ASN.1 OBJECT IDENTIFIERs for equality
 *
 * @param[in] a      First OID node array to compare
 * @param[in] a_cnt  Number of nodes in first OID
 * @param[in] b      Second OID node array to compare
 * @param[in] b_cnt  Number of nodes in second OID
 * @return 1 if OIDs are equal, 0 if not equal or invalid parameters
 */
int XMen_lib_asn1_sm2_object_identifier_equ(const uint32_t* a, uint32_t a_cnt, const uint32_t* b, uint32_t b_cnt)
{
	if (!a || a_cnt < ASN1_OID_MIN_NODES || a_cnt > ASN1_OID_MAX_NODES
		|| !b || b_cnt < ASN1_OID_MIN_NODES || b_cnt > ASN1_OID_MAX_NODES) {
		return 0;
	}
	if (a_cnt != b_cnt || memcmp(a, b, b_cnt * sizeof(uint32_t))) {
		return 0;
	}
	return 1;
}


const ASN1_OID_INFO* XMen_lib_asn1_sm2_oid_info_from_name(const ASN1_OID_INFO* infos, uint32_t infos_cnt, const char* name)
{
	uint32_t i;

	if (!infos || !infos_cnt || !name) {
		return NULL;
	}
	for (i = 0; i < infos_cnt; i++) {
		if (strcmp(infos[i].name, name) == 0) {
			return &infos[i];
		}
	}
	return NULL;
}


/*
 * Find ASN.1 OID info by OID value
 *
 * @param[in] infos      Array of ASN.1 OID info structures
 * @param[in] infos_cnt  Number of entries in infos array
 * @param[in] oid        OID value to look up
 * @return Pointer to matching OID info entry, NULL if not found or invalid parameters
 */
const ASN1_OID_INFO* XMen_lib_asn1_sm2_oid_info_from_oid(const ASN1_OID_INFO* infos, uint32_t infos_cnt, int oid)
{
	uint32_t i;

	if (!infos || !infos_cnt || oid < 0) {
		return NULL;
	}
	for (i = 0; i < infos_cnt; i++) {
		if (infos[i].oid == oid) {
			return &infos[i];
		}
	}
	return NULL;
}

/*
 * Parse a UTF-8 character from a byte stream
 *
 * @param[out] c       Pointer to store decoded UTF-8 character value
 * @param[in,out] pin  Pointer to input byte stream, updated after reading character
 * @param[in,out] pinlen Length of remaining input bytes, updated after reading
 * @return 1 on success, 0 if no input available, -1 on invalid UTF-8 encoding
 */
static int XMen_lib_asn1_sm2_utf8char_from_bytes(uint32_t* c, const uint8_t** pin, uint32_t* pinlen)
{
    uint32_t utf8char;
    const uint8_t* in = *pin;
    uint32_t inlen = *pinlen;
    uint32_t utf8char_len, i;

    
    if (!inlen) {
        return 0;
    }

    
    if ((in[0] & 0x80) == 0x00) {
        utf8char_len = 1;
    }
    else if ((in[0] & 0xe0) == 0xc0) {
        utf8char_len = 2;
    }
    else if ((in[0] & 0xf0) == 0xe0) {
        utf8char_len = 3;
    }
    else if ((in[0] & 0xf8) == 0xf0) {
        utf8char_len = 4;
    }
    else {
        return -1; 
    }

    
    if (inlen < utf8char_len) {
        return -1;
    }

    
    utf8char = in[0];

    
    for (i = 1; i < utf8char_len; i++) {
        if ((in[i] & 0x60) != 0x80) {
            return -1;
        }
        utf8char = (utf8char << 8) | in[i];
    }

    
    *c = utf8char;
    (*pin) += utf8char_len;
    (*pinlen) -= utf8char_len;
    return 1;
}

/*
 * Check if a string is valid UTF-8 encoded
 *
 * @param[in] a      String to check for UTF-8 encoding
 * @param[in] alen   Length of string in bytes
 * @return 1 if string is valid UTF-8, 0 if invalid or empty
 */
int XMen_lib_asn1_sm2_string_is_utf8_string(const char* a, uint32_t alen)
{
	uint32_t utf8char;

	if (!a || !alen) {
		return 0;
	}
	while (alen) {
		if (XMen_lib_asn1_sm2_utf8char_from_bytes(&utf8char, (const uint8_t**)&a, &alen) != 1) {
			return 0;
		}
	}
	return 1;
}



/*
 * Encode UTF-8 string to DER encoding with custom tag
 *
 * @param[in] tag     ASN.1 tag value to use
 * @param[in] d       UTF-8 string to encode
 * @param[in] dlen    Length of UTF-8 string in bytes
 * @param[out] out    Output buffer to write encoded data, NULL for length only
 * @param[out] outlen Length of encoded output
 * @return 1 on success, -1 on error
 */
int XMen_lib_asn1_sm2_utf8_string_to_der_ex(int tag, const char* d, uint32_t dlen, uint8_t** out, uint32_t* outlen)
{
	/* Validate input is valid UTF-8 */
	if (XMen_lib_asn1_sm2_string_is_utf8_string(d, dlen) != 1) {
		return -1;
	}

	/* Encode as ASN.1 type */
	int ret = XMen_lib_asn1_sm2_type_to_der(tag, (const uint8_t*)d, dlen, out, outlen);
	if (ret != 1) {
		return ret;
	}

	return 1;
}

/*
 * Decode UTF-8 string from DER encoding with custom tag
 *
 * @param[in] tag     Expected ASN.1 tag value
 * @param[out] a      Pointer to store decoded UTF-8 string
 * @param[out] alen   Length of decoded UTF-8 string
 * @param[in,out] in  Input buffer containing DER encoded data
 * @param[in,out] inlen Length of input buffer, updated after decoding
 * @return 1 on success, -1 on error
 */
int XMen_lib_asn1_sm2_utf8_string_from_der_ex(int tag, const char** a, uint32_t* alen, const uint8_t** in, uint32_t* inlen)
{
	int ret;
	/* Decode as ASN.1 type */
	ret = XMen_lib_asn1_sm2_type_from_der(tag, (const uint8_t**)a, alen, in, inlen);
	if (ret != 1) {
		return ret < 0 ? ret : -1;
	}

	/* Validate decoded data is valid UTF-8 */
	if (*a == NULL || *alen == 0u || XMen_lib_asn1_sm2_string_is_utf8_string(*a, *alen) != 1) {
		return -1;
	}

	return 1;
}

/*
 * Check if a string is valid IA5String (ASCII)
 *
 * @param[in] a      String to check
 * @param[in] alen   Length of string in bytes
 * @return 1 if string is valid IA5String, 0 if invalid or empty
 */
int XMen_lib_asn1_sm2_string_is_ia5_string(const char* a, uint32_t alen)
{
	uint32_t i;
	for (i = 0; i < alen; i++) {
	/* Check each character is 7-bit ASCII */
	/* TODO: Add validation logic */
	/* if ((a[i] & 0x80) || !isprint(a[i])) return 0; */
	}
	return 1;
}

/*
 * Encode IA5String to DER encoding with custom tag
 *
 * @param[in] tag     ASN.1 tag value to use
 * @param[in] d       IA5String to encode
 * @param[in] dlen    Length of IA5String in bytes
 * @param[out] out    Output buffer to write encoded data, NULL for length only
 * @param[out] outlen Length of encoded output
 * @return 1 on success, -1 on error
 */
int XMen_lib_asn1_sm2_ia5_string_to_der_ex(int tag, const char* d, uint32_t dlen, uint8_t** out, uint32_t* outlen)
{
	int ret;
	/* Validate input is valid IA5String */
	if (XMen_lib_asn1_sm2_string_is_ia5_string(d, dlen) != 1) {
		return -1;
	}

	/* Encode as ASN.1 type */
	ret = XMen_lib_asn1_sm2_type_to_der(tag, (const uint8_t*)d, dlen, out, outlen);
	if (ret != 1) {
		return ret;
	}

	return 1;
}

/*
 * Decode ASN.1 IA5String from DER encoding with custom tag
 *
 * @param[in] tag     ASN.1 tag value to use
 * @param[out] a      Pointer to store decoded IA5String
 * @param[out] alen   Length of decoded IA5String
 * @param[in,out] in  Input buffer containing DER encoded data, updated after decoding
 * @param[in,out] inlen Length of remaining input, updated after decoding
 * @return 1 on success, 0 if tag doesn't match, -1 on error
 */
int XMen_lib_asn1_sm2_ia5_string_from_der_ex(int tag, const char** a, uint32_t* alen, const uint8_t** in, uint32_t* inlen)
{
    int ret;

    /* Decode ASN.1 type */
    ret = XMen_lib_asn1_sm2_type_from_der(tag, (const uint8_t**)a, alen, in, inlen);
    if (ret != 1) {
        return ret < 0 ? ret : -1;
    }

    /* Validate decoded string is valid IA5String */
    if (*a == NULL || *alen == 0u || XMen_lib_asn1_sm2_string_is_ia5_string(*a, *alen) != 1) {
        return -1;
    }

    /* Success */
    return 1;
}

/*
 * Check if a year is a leap year
 *
 * @param[in] year  Year to check
 * @return 1 if leap year, 0 if not
 */
static int is_leap_year(int year) {
	return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}

/* Helper macro to convert char digit to integer */
#define val(c)	((c)-'0')

/*
 * Convert ASN.1 time string to Unix timestamp
 *
 * @param[in] utc_time   Flag indicating if string is UTCTime (1) or GeneralizedTime (0)
 * @param[out] timestamp Pointer to store Unix timestamp
 * @param[in] str        Time string to parse
 * @return 1 on success, -1 on error
 */
int XMen_lib_asn1_sm2_time_from_str(int utc_time, time_t* timestamp, const char* str)
{
    /* Length of time string for each format */
    int time_str_len[2] = { 15, 13 };
    
    /* Days in normal and leap years */
    int days_per_year[2] = { 365, 366 };
    
    /* Days in each month (index 0 unused) */
    int days_per_month[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
    
    int year, month, day, hour, minute, second;
    const char* p = str;
    int i;

    /* Normalize utc_time flag to 0 or 1 */
    utc_time &= 1;

    /* Validate all characters are digits except last 'Z' */
    for (i = 0; i < time_str_len[utc_time] - 1; i++) {
        if (!('0' <= str[i] && str[i] <= '9')) {
            return -1;
        }
    }

    /* Check for required 'Z' timezone indicator */
    if (str[i] != 'Z') {
        return -1;
    }

    /* Parse year field based on format */
    if (utc_time) {
        year = val(p[0]) * 10 + val(p[1]);
        year += (year <= 50) ? 2000 : 1900;
        p += 2;
    } else {
        year = val(p[0]) * 1000 + val(p[1]) * 100 + val(p[2]) * 10 + val(p[3]);
        p += 4;
    }

    /* Update February days for leap years */
    if (is_leap_year(year)) {
        days_per_month[2] = 29;
    }

    /* Parse remaining time fields */
    month = val(p[0]) * 10 + val(p[1]);
    p += 2;
    day = val(p[0]) * 10 + val(p[1]);
    p += 2;
    hour = val(p[0]) * 10 + val(p[1]);
    p += 2;
    minute = val(p[0]) * 10 + val(p[1]);
    p += 2;
    second = val(p[0]) * 10 + val(p[1]);
    p += 2;

    /* Validate all fields are within valid ranges */
    if (year < 1970 || month < 1 || month > 12 || day < 1 || day > days_per_month[month] ||
        hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 59) {
        return -1;
    }

    /* Convert to zero-based day */
    day--;

    /* Add days for all years since 1970 */
    while (year-- > 1970) {
        day += days_per_year[is_leap_year(year)];
    }

    /* Add days for all months in current year */
    while (month-- > 1) {
        day += days_per_month[month];
    }

    /* Convert to Unix timestamp */
    *timestamp = (time_t)day * 86400 + hour * 3600 + minute * 60 + second;

    /* Success */
    return 1;
}


/*
 * Convert Unix timestamp to ASN.1 time string
 *
 * @param[in] utc_time   Flag indicating if UTC time (1) or Generalized time (0)
 * @param[in] timestamp  Unix timestamp to convert
 * @param[out] str       Output buffer to store time string
 * @return 1 on success, -1 on error
 */
int XMen_lib_asn1_sm2_time_to_str(int utc_time, time_t timestamp, char* str)
{
    /* Arrays for days in each month and year */
    int days_per_month[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
    int days_per_year[2] = { 365, 366 };
    int max_year[2] = { 9999, 2050 };
    int year, month, second, hour, minute;
    time_t day;
    char* p = str;

    /* Normalize utc_time flag */
    utc_time &= 1;

    /* Convert timestamp to days and seconds */
    day = timestamp / 86400;
    second = timestamp % 86400;

    /* Calculate year */
    for (year = 1970; year <= max_year[utc_time]; year++) {
        if (day < days_per_year[is_leap_year(year)]) {
            break;
        }
        day -= days_per_year[is_leap_year(year)];
    }
    if (year > max_year[utc_time]) {
        return -1;
    }

    /* Adjust day to 1-based */
    day++;

    /* Update February days for leap years */
    if (is_leap_year(year)) {
        days_per_month[2] = 29;
    }

    /* Calculate month */
    for (month = 1; month <= 12; month++) {
        if (day <= days_per_month[month]) {
            break;
        }
        day -= days_per_month[month];
    }

    /* Calculate hour, minute, second */
    hour = second / 3600;
    second %= 3600;
    minute = second / 60;
    second %= 60;

    /* Initialize output string with zeros */
    memset(p, '0', utc_time ? 12 : 14);

    /* Format year - 4 digits for generalized time */
    if (!utc_time) {
        p[0] += (year / 100) / 10;
        p[1] += (year / 100) % 10;
        p += 2;
    }

    /* Format remaining time fields */
    year %= 100;
    p[0] += year / 10;
    p[1] += year % 10;
    p[2] += month / 10;
    p[3] += month % 10;
    p[4] += day / 10;
    p[5] += day % 10;
    p[6] += hour / 10;
    p[7] += hour % 10;
    p[8] += minute / 10;
    p[9] += minute % 10;
    p[10] += second / 10;
    p[11] += second % 10;
    p[12] = 'Z';

    return 1;
}

/*
 * Encode ASN.1 UTCTime to DER encoding with custom tag
 *
 * @param[in] tag     ASN.1 tag value to use
 * @param[in] a       Unix timestamp to encode
 * @param[out] out    Output buffer to write encoded data, NULL for length only
 * @param[out] outlen Length of encoded output
 * @return 1 on success, 0 if no timestamp provided, -1 on error
 */
int XMen_lib_asn1_sm2_utc_time_to_der_ex(int tag, time_t a, uint8_t** out, uint32_t* outlen)
{
    /* Buffer to store formatted time string */
    char buf[ASN1_UTC_TIME_STRLEN + 1] = { 0 };
    int utc_time = 1;

    /* Validate parameters */
    if (!outlen) {
        return -1;
    }
    if (a == -1) {
        return 0;
    }

    /* Convert timestamp to string */
    if (XMen_lib_asn1_sm2_time_to_str(utc_time, a, buf) != 1) {
        return -1;
    }

    /* Write tag byte */
    if (out && *out) {
        *(*out)++ = tag;
    }
    (*outlen)++;

    /* Write length */
    XMen_lib_asn1_sm2_len_to_der(ASN1_UTC_TIME_STRLEN, out, outlen);

    /* Write time string */
    if (out && *out) {
        memcpy(*out, buf, ASN1_UTC_TIME_STRLEN);
        (*out) += ASN1_UTC_TIME_STRLEN;
    }
    *outlen += ASN1_UTC_TIME_STRLEN;

    return 1;
}


/*
 * Count occurrences of ASN.1 types with specified tag in data
 *
 * @param[in] d      Input data buffer to search
 * @param[in] dlen   Length of input data
 * @param[in] tag    ASN.1 tag value to count
 * @param[out] cnt   Number of matching tag occurrences found
 * @return 1 on success, -1 on error
 */
int XMen_lib_asn1_sm2_types_get_count(const uint8_t* d, uint32_t dlen, int tag, uint32_t* cnt)
{
	int item_tag;
	const uint8_t* item_d;
	uint32_t item_dlen;

	/* Validate parameters */
	if (!d || !cnt) {
		return -1;
	}
	*cnt = 0;

	/* Scan through data counting matching tags */
	while (dlen) {
		if (XMen_lib_asn1_sm2_any_type_from_der(&item_tag, &item_d, &item_dlen, &d, &dlen) != 1) {
			return -1;
		}
		if (item_tag != tag) {
			return -1;
		}
		(*cnt)++;
	}
	return 1;
}

/*
 * Get ASN.1 type item by index from data buffer
 *
 * @param[in] d         Input data buffer to search
 * @param[in] dlen      Length of input data
 * @param[in] tag       ASN.1 tag value to find
 * @param[in] index     Zero-based index of item to retrieve
 * @param[out] item_d   Pointer to found item data
 * @param[out] item_dlen Length of found item data
 * @return 1 on success, -1 on error or item not found
 */
int XMen_lib_asn1_sm2_types_get_item_by_index(const uint8_t* d, uint32_t dlen, int tag,
	int index, const uint8_t** item_d, uint32_t* item_dlen)
{
	int a_tag;
	const uint8_t* a_d;
	uint32_t a_dlen;
	int i = 0;

	/* Validate parameters */
	if (!d || !item_d || !item_dlen) {
		return -1;
	}

	/* Scan through data to find item at index */
	while (dlen) {
		if (XMen_lib_asn1_sm2_any_type_from_der(&a_tag, &a_d, &a_dlen, &d, &dlen) != 1) {
			return -1;
		}
		if (a_tag != tag) {
			return -1;
		}
		if (i++ == index) {
			*item_d = d;
			*item_dlen = dlen;
			return 1;
		}
	}

	return -1;
}

/*
 * Check if expression is true
 *
 * @param[in] expr   Expression to evaluate
 * @return 1 if expression is true, -1 if false
 */
int XMen_lib_asn1_sm2_check(int expr)
{
	if (expr)
		return 1;

	return -1;
}

/*
 * Check if length is zero
 *
 * @param[in] len    Length value to check
 * @return 1 if length is zero, -1 if non-zero
 */
int XMen_lib_asn1_sm2_length_is_zero(uint32_t len)
{
	if (len) {
		return -1;
	}
	return 1;
}

/*
 * Check if first length is less than or equal to second length
 *
 * @param[in] len1   First length to compare
 * @param[in] len2   Second length to compare
 * @return 1 if len1 <= len2, -1 if len1 > len2
 */
int XMen_lib_asn1_sm2_length_le(uint32_t len1, uint32_t len2)
{
	if (len1 > len2) {
		return -1;
	}
	return 1;
}

/*
 * XOR two memory buffers
 *
 * @param[out] r     Output buffer for XOR result
 * @param[in] a      First input buffer
 * @param[in] b      Second input buffer
 * @param[in] len    Length of buffers
 */
void XMen_lib_sm2_memxor(void* r, const void* a, const void* b, uint32_t len)
{
	uint8_t* pr = (uint8_t*) r;
	const uint8_t* pa = (uint8_t*)a;
	const uint8_t* pb = (uint8_t*)b;
	uint32_t i;

	/* XOR bytes from both inputs */
	for (i = 0; i < len; i++) {
		pr[i] = pa[i] ^ pb[i];
	}
}

/*
 * Constant-time memory comparison
 *
 * @param[in] in_a   First memory buffer to compare
 * @param[in] in_b   Second memory buffer to compare
 * @param[in] len    Length of buffers to compare
 * @return 0 if buffers match, non-zero if different
 */
int gmssl_secure_memcmp(const volatile void* volatile in_a, const volatile void* volatile in_b, uint32_t len)
{
	uint32_t i;
	const volatile unsigned char* a = (unsigned char*)in_a;
	const volatile unsigned char* b = (unsigned char*)in_b;
	unsigned char x = 0;

	/* Compare all bytes in constant time */
	for (i = 0; i < len; i++) {
		x |= a[i] ^ b[i];
	}

	return x;
}

/* Function pointer type for memset-like operations */
typedef void* (*memset_t)(void*, int, size_t);

/* Volatile pointer to memset function to prevent optimization */
static volatile memset_t memset_func = memset;

/*
 * Securely clear memory buffer by zeroing contents
 *
 * @param[in] ptr  Pointer to memory buffer to clear
 * @param[in] len  Length of buffer in bytes
 */
void secure_clear(void* ptr, size_t len)
{
	memset_func(ptr, 0, len);
}

/*
 * Check if memory buffer contains all zeros
 *
 * @param[in] buf  Buffer to check
 * @param[in] len  Length of buffer in bytes
 * @return 1 if buffer is all zeros, 0 otherwise
 */
int mem_is_zero(const uint8_t* buf, uint32_t len)
{
	int ret = 1;
	uint32_t i;
	/* Check each byte, clear ret if non-zero found */
	for (i = 0; i < len; i++) {
		if (buf[i]) ret = 0;
	}
	return ret;
}
